chore: epic comment add action rework#113
Conversation
WalkthroughShifts epic comment creation to a modal-driven UForm workflow with validation. Updates page to open a modal, adds a new modal component, refactors the form, trims text on the server, removes store.addComment, and applies small UI/semantic tweaks (button card, relative section). Adds ru-RU translations. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant P as Epic Page
participant O as Overlay Controller
participant M as ModalCreateEpicComment
participant F as FormCreateEpicComment
participant S as Server API (/api/epic/id/:id/comment)
participant ES as Epic Store
participant US as User Store
U->>P: Click "Create comment" card
P->>O: overlay.create(ModalCreateEpicComment)
P->>M: Open with { epicId }
M->>F: Render form (UForm + validator)
U->>F: Enter text + Submit
F->>S: POST { epicId, text } with Authorization
S-->>F: 200 OK (created)
F->>ES: update()
F->>US: update()
F-->>M: emit submitted
F-->>M: emit success
M->>O: closeAll()
O-->>U: Modal closes
alt Error
S-->>F: 4xx/5xx
F-->>U: Feedback error vibration
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
apps/atrium-telegram/app/pages/epic/[epicId]/index.vue (1)
26-29: Two nits: redundant check and Cyrillic letter typo.
length >= 0is always true; use> 0or drop thev-if.- Replace "комментарий" with "комментарий" (proper "й").
Apply this diff:
- <div v-if="epic?.comments && epic?.comments.length >= 0" class="flex flex-row items-center gap-2"> + <div v-if="epic?.comments && epic?.comments.length > 0" class="flex flex-row items-center gap-2"> <UIcon name="i-lucide-message-circle" class="size-5" /> - {{ epic.comments.length }} {{ pluralizationRu(epic.comments.length, ['комментарий', 'комментария', 'комментариев']) }} + {{ epic.comments.length }} {{ pluralizationRu(epic.comments.length, ['комментарий', 'комментария', 'комментариев']) }} </div>apps/atrium-telegram/app/components/form/CreateEpicComment.vue (1)
18-28: Disable submit on whitespace-only input.Current check
!state.textenables the button for" ". Use a trimmed check to align with server-side guard.Apply this diff:
- :disabled="!state.text" + :disabled="!(state.text && state.text.trim().length > 0)"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (8)
apps/atrium-telegram/app/components/ActiveCard.vue(1 hunks)apps/atrium-telegram/app/components/Section.vue(1 hunks)apps/atrium-telegram/app/components/form/CreateEpicComment.vue(1 hunks)apps/atrium-telegram/app/components/modal/CreateEpicComment.vue(1 hunks)apps/atrium-telegram/app/pages/epic/[epicId]/index.vue(2 hunks)apps/atrium-telegram/app/stores/epic.ts(0 hunks)apps/atrium-telegram/i18n/locales/ru-RU.json(1 hunks)apps/atrium-telegram/server/api/epic/id/[epicId]/comment.post.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/atrium-telegram/app/stores/epic.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (4)
apps/atrium-telegram/app/components/Section.vue (1)
2-2: Confirm positioning context change is intendedMaking Section’s root
relativemeans absolutely positioned children will anchor to Section (not the outer card). Verify this won’t shift overlays/badges now that ActiveCard’s wrapper is alsorelative.apps/atrium-telegram/app/components/ActiveCard.vue (1)
2-6: ActiveCard usage audit: no nested interactive controls detected
Repository-wide search confirmed no<ActiveCard>instances wrap<button>,<a>,<input>,<select>, or<textarea>, nor appear inside<form>elements.apps/atrium-telegram/server/api/epic/id/[epicId]/comment.post.ts (1)
23-24: Assert authenticated user exists (defensive).This assumes
event.context.user.idis always set. If a route-level guard isn’t guaranteed, add an explicit 401 or assert here.Would you confirm this endpoint is behind auth middleware that always populates
event.context.user?apps/atrium-telegram/app/pages/epic/[epicId]/index.vue (1)
41-46: LGTM: Modal trigger wiring looks correct.
overlay.create(ModalCreateEpicComment)+.open({ epicId })is consistent with the new flow.
| @@ -1,9 +1,9 @@ | |||
| <template> | |||
| <div class="relative active:scale-95 duration-200" @click="vibrate()"> | |||
| <button class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()"> | |||
There was a problem hiding this comment.
Add type="button" to prevent unintended form submissions
Without an explicit type, buttons default to submit inside forms.
Apply this diff:
- <button class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">
+ <button type="button" class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()"> | |
| <button type="button" class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()"> |
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/ActiveCard.vue around line 2, the button
lacks an explicit type so it will default to "submit" when placed inside a form;
update the button element to include type="button" to prevent unintended form
submissions while preserving existing classes and the @click handler.
🛠️ Refactor suggestion
Restore visible focus styles; replace outline-0 with a custom focus-visible ring (and smooth the scale animation)
outline-0 removes the default focus indicator; add an explicit focus style. Also add transition-transform so active:scale-95 animates.
Apply this diff:
- <button type="button" class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">
+ <button type="button" class="relative w-full active:scale-95 transition-transform duration-200 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-indigo-500" @click="vibrate()">If you have design tokens (e.g., tg- colors), swap indigo-500/offsets accordingly.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/ActiveCard.vue around line 2, the button
currently removes the browser focus indicator with outline-0 and lacks a
transform transition; restore an accessible visible focus style and smooth the
active scale by replacing outline-0 with a focus-visible ring class (e.g.,
focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-indigo-500
focus-visible:ring-offset-white or the equivalent tg- token colors) and add
transition-transform to the class list so active:scale-95 animates smoothly;
ensure ring-offset color matches the component background and adjust token names
to your design system if present.
| <UForm | ||
| :validate="createValidator(createEpicCommentSchema)" | ||
| :state="state" |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Missing/implicit createValidator import — verify auto-import.
createValidator is used but not imported; unless auto-imported, this will throw at runtime.
Run to locate its definition and usages:
If not auto-imported, add the proper import path used elsewhere in the project.
🏁 Script executed:
#!/bin/bash
# Find a definition/export of createValidator
rg -nP 'export\s+(?:const|function)\s+createValidator\b|createValidator:\s*' -C2
# Check for a Nuxt auto-import declaration
rg -nP 'autoImports|imports:\s*\{[^}]*imports' -g '!**/node_modules/**' -C3
# Where else is it used (to infer import style)
rg -nP '\bcreateValidator\s*\(' -g '!**/node_modules/**' -C2Length of output: 19492
Add missing createValidator import
In apps/atrium-telegram/app/components/form/CreateEpicComment.vue’s <script setup> block, add:
import { createValidator } from '~/utils/ui'createValidator is exported from app/utils/ui.ts and must be imported here to avoid a runtime error.
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/form/CreateEpicComment.vue around lines 2
to 4, the template uses createValidator(createEpicCommentSchema) but the
createValidator function is not imported; add an import statement in the <script
setup> block: import { createValidator } from '~/utils/ui' so the component can
call createValidator at runtime without error.
| <FormCreateEpicComment | ||
| :epic-id="epicId" | ||
| @submitted="overlay.closeAll" | ||
| @success="overlay.closeAll" | ||
| /> |
There was a problem hiding this comment.
Don’t close the modal on submit start; close only on success.
Closing on @submitted hides errors and prevents retry UX.
Apply this diff:
<FormCreateEpicComment
:epic-id="epicId"
- @submitted="overlay.closeAll"
@success="overlay.closeAll"
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <FormCreateEpicComment | |
| :epic-id="epicId" | |
| @submitted="overlay.closeAll" | |
| @success="overlay.closeAll" | |
| /> | |
| <FormCreateEpicComment | |
| :epic-id="epicId" | |
| @success="overlay.closeAll" | |
| /> |
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/modal/CreateEpicComment.vue around lines
4 to 8, the modal is being closed on the `@submitted` event which hides
validation/errors and blocks retry; remove the `@submitted="overlay.closeAll"`
binding and only call `overlay.closeAll` on the `@success` event so the modal
stays open while submission is in progress or failed and only closes after a
confirmed success.
| "epic-comment": { | ||
| "button": "Создать комментарий", | ||
| "title": "Создание комментария" | ||
| }, |
There was a problem hiding this comment.
Fix Cyrillic letter in “комментарий” (combining mark currently used).
The button label uses "комментарий" (и + combining breve). Replace with the correct single character "й".
Apply this diff:
"epic-comment": {
- "button": "Создать комментарий",
+ "button": "Создать комментарий",
"title": "Создание комментария"
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "epic-comment": { | |
| "button": "Создать комментарий", | |
| "title": "Создание комментария" | |
| }, | |
| "epic-comment": { | |
| "button": "Создать комментарий", | |
| "title": "Создание комментария" | |
| }, |
🤖 Prompt for AI Agents
In apps/atrium-telegram/i18n/locales/ru-RU.json around lines 43-46, the button
label uses a Cyrillic "и" followed by a combining breve (resulting in
"комментарий"); replace that sequence with the single precomposed Cyrillic
character "й" so the string becomes "Создать комментарий". Ensure the file is
saved with UTF-8 encoding and no combining marks remain in the value.
| const comment = await repository.epic.createComment({ | ||
| ...data, | ||
| text: data.text.trim(), | ||
| userId: event.context.user.id, | ||
| epicId, | ||
| }) |
There was a problem hiding this comment.
Prevent whitespace-only comments (trim happens after validation).
If the schema allows a single character, strings like " " will pass validation and be saved as empty after trim. Trim first and reject empty.
Apply this diff:
- const comment = await repository.epic.createComment({
- text: data.text.trim(),
- userId: event.context.user.id,
- epicId,
- })
+ const text = data.text.trim()
+ if (!text) {
+ throw createError({ statusCode: 400, message: 'Text is required' })
+ }
+ const comment = await repository.epic.createComment({
+ text,
+ userId: event.context.user.id,
+ epicId,
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const comment = await repository.epic.createComment({ | |
| ...data, | |
| text: data.text.trim(), | |
| userId: event.context.user.id, | |
| epicId, | |
| }) | |
| // Trim input and reject empty text before creation | |
| const text = data.text.trim() | |
| if (!text) { | |
| throw createError({ statusCode: 400, message: 'Text is required' }) | |
| } | |
| const comment = await repository.epic.createComment({ | |
| text, | |
| userId: event.context.user.id, | |
| epicId, | |
| }) |
🤖 Prompt for AI Agents
In apps/atrium-telegram/server/api/epic/id/[epicId]/comment.post.ts around lines
21-25, the code trims the comment text only when creating the record which
allows whitespace-only strings to pass earlier validation; change the flow to
trim data.text immediately (e.g., const text = (data.text || "").trim()),
validate that text is non-empty and reject with a 4xx error if empty, then pass
the trimmed text into repository.epic.createComment so only non-empty, trimmed
comments are persisted.



Summary by CodeRabbit